<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
    />

    <!-- Metas -->
    <title>curtains.js | Home</title>
    <meta
      name="description"
      content="curtains.js is a lightweight vanilla WebGL javascript library that turns HTML DOM elements into interactive textured planes."
    />
    <link rel="canonical" href="https://www.curtainsjs.com/" />

    <!-- Facebook OG -->
    <meta property="og:title" content="curtains.js | Home" />
    <meta property="og:type" content="website" />
    <meta
      property="og:description"
      content="curtains.js is a lightweight vanilla WebGL javascript library that turns HTML DOM elements into interactive textured planes."
    />
    <meta property="og:url" content="https://www.curtainsjs.com/" />
    <meta
      property="og:image"
      content="https://www.curtainsjs.com/images/curtains-js-logo.jpg"
    />

    <!-- Twitter card -->
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:site" content="@martinlaxenaire" />
    <meta name="twitter:creator" content="@martinlaxenaire" />
    <meta name="twitter:title" content="curtains.js | Home" />
    <meta
      name="twitter:description"
      content="curtains.js is a lightweight vanilla WebGL javascript library that turns HTML DOM elements into interactive textured planes."
    />
    <meta
      name="twitter:image"
      content="https://www.curtainsjs.com/images/curtains-js-logo.jpg"
    />

    <!-- Favicon -->
    <link
      rel="apple-touch-icon"
      sizes="180x180"
      href="images/favicons/apple-touch-icon.png"
    />
    <link
      rel="icon"
      type="image/png"
      sizes="32x32"
      href="images/favicons/favicon-32x32.png"
    />
    <link
      rel="icon"
      type="image/png"
      sizes="16x16"
      href="images/favicons/favicon-16x16.png"
    />
    <link rel="manifest" href="images/favicons/site.webmanifest" />
    <link
      rel="mask-icon"
      href="images/favicons/safari-pinned-tab.svg"
      color="#202340"
    />
    <link rel="shortcut icon" href="images/favicons/favicon.ico" />
    <meta name="msapplication-TileColor" content="#202340" />
    <meta
      name="msapplication-config"
      content="images/favicons/browserconfig.xml"
    />
    <meta name="theme-color" content="#202340" />

    <link
      href="https://fonts.googleapis.com/css?family=PT+Sans:400,700"
      rel="stylesheet"
    />
    <link
      href="https://fonts.googleapis.com/css?family=Abril+Fatface"
      rel="stylesheet"
    />
    <link
      href="https://fonts.googleapis.com/css?family=Roboto+Mono&display=swap"
      rel="stylesheet"
    />
    <link rel="stylesheet" href="style.css" type="text/css" />
  </head>
  <body>
    <div id="page-wrap">
      <div id="canvas"></div>

      <div id="content">
        <header id="header">
          <div class="wrapper">
            <div id="header-wrapper" class="flex-wrapper">
              <div id="header-title">
                <a href="/" title="Home">curtains.js</a>
              </div>
              <nav id="main-menu">
                <ul class="flex-wrapper">
                  <li>
                    <a href="get-started.html">Get started</a>
                  </li>
                  <li>
                    <a href="documentation.html">Docs</a>
                  </li>
                  <li>
                    <a href="download.html">Download</a>
                  </li>
                </ul>
              </nav>
            </div>
          </div>
        </header>

        <section id="intro" class="flex-wrapper">
          <h1 id="site-title">curtains.js</h1>
          <h2 id="site-subtitle">
            easy WebGL tool to animate images and videos
          </h2>

          <div class="curtain" data-vs-id="intro-vs" data-fs-id="intro-fs">
            <img
              data-sampler="curtainSampler"
              src="images/intro-background.jpg"
            />
          </div>
        </section>

        <section class="content-section">
          <div class="wrapper">
            <div class="text-section">
              <h2>What is it ?</h2>
              <p>
                Shaders are the new front-end web development big thing, with
                the ability to create very powerful 3D interactions and
                animations. A lot of very good javascript libraries already
                handle WebGL but with most of them it's kind of a headache to
                position your meshes relative to the DOM elements of your web
                page.
              </p>
              <p>
                curtains.js was created with just that issue in mind. It is a
                small vanilla WebGL javascript library that converts HTML
                elements containing images, videos and canvases into 3D WebGL
                textured planes, allowing you to animate them via shaders.
              </p>
              <p>
                You can define each plane size and position via CSS, which makes
                it super easy to add WebGL responsive planes all over your
                pages.
              </p>
              <p>
                You can also add post processing effects to your scene to spice
                up the whole thing.
              </p>
              <p>
                Be sure to check the
                <a href="documentation.html" title="curtains.js documentation"
                  >documentation</a
                >
                and examples to see what's possible.
              </p>
            </div>

            <div class="text-section">
              <h2>Download</h2>
              <p>
                <a
                  href="dist/curtains.umd.js"
                  id="download-full-lib-link"
                  download
                  target="_blank"
                  title="download curtains.js v8.1"
                  >curtains.umd.js v8.1 (346Ko)</a
                >
                -
                <a
                  href="dist/curtains.umd.min.js"
                  id="download-min-lib-link"
                  download
                  target="_blank"
                  title="download curtains.umd.min.js v8.1"
                  >curtains.umd.min.js v8.1 (122Ko)</a
                >
                -
                <a
                  href="https://github.com/martinlaxenaire/curtainsjs"
                  title="curtains.js on Github"
                  target="_blank"
                  >Github</a
                >.
              </p>
            </div>

            <div class="text-section">
              <h2 id="examples">Examples</h2>
              <p>
                All the examples are fully commented: do not hesitate to have a
                look at the source code.
              </p>
              <h3>Images</h3>
              <p>
                <a
                  href="examples/vertex-coords-helper/index.html"
                  title="Vertex coordinates helper"
                  target="_blank"
                  >Vertex coordinates helper</a
                ><br />
                <a
                  href="examples/plane-properties-transforms-cheat-sheet/index.html"
                  title="Plane properties and transformations cheat sheet"
                  target="_blank"
                  >Plane properties and transformations cheat sheet</a
                ><br />
                <a
                  href="examples/simple-plane/index.html"
                  title="Simple plane"
                  target="_blank"
                  >Simple plane</a
                ><br />
                <a
                  href="examples/multiple-textures/index.html"
                  title="Slideshow with a displacement shader"
                  target="_blank"
                  >Slideshow with a displacement shader</a
                ><br />
                <a
                  href="examples/multiple-planes/index.html"
                  title="Multiple planes"
                  target="_blank"
                  >Multiple planes</a
                ><br />
                <a
                  href="examples/multiple-planes-scroll-effect/index.html"
                  title="Multiple planes scroll effect : rotation, scale and parallax"
                  target="_blank"
                  >Multiple planes scroll effect : rotation, scale and
                  parallax</a
                ><br />
                <a
                  href="examples/asynchronous-textures/index.html"
                  title="Asynchronous textures loading"
                  target="_blank"
                  >Asynchronous textures loading</a
                ><br />
                <a
                  href="examples/ajax-navigation-with-plane-removal/index.html"
                  title="AJAX navigation with plane removal"
                  target="_blank"
                  >AJAX navigation with plane removal</a
                ><br />
              </p>
              <h3>Videos</h3>
              <p>
                <a
                  href="examples/simple-video-plane/index.html"
                  title="Simple video plane"
                  target="_blank"
                  >Simple video plane</a
                ><br />
                <a
                  href="examples/multiple-video-textures/index.html"
                  title="Multiple video textures"
                  target="_blank"
                  >Multiple video textures with a displacement shader</a
                ><br />
              </p>
              <h3>Canvas</h3>
              <p>
                <a
                  href="examples/simple-canvas-plane/index.html"
                  title="Simple canvas plane"
                  target="_blank"
                  >Simple canvas plane</a
                ><br />
                <a
                  href="examples/multiple-planes-canvas-text/index.html"
                  title="Text planes using canvas"
                  target="_blank"
                  >Text planes using canvas</a
                >
              </p>
              <h3>Post processing</h3>
              <p>
                <a
                  href="examples/post-processing-displacement/index.html"
                  title="Post processing displacement effect"
                  target="_blank"
                  >Post processing displacement effect</a
                ><br />
                <a
                  href="examples/post-processing-scroll-effect/index.html"
                  title="Post processing multiple passes"
                  target="_blank"
                  >Post processing multiple passes</a
                ><br />
                <a
                  href="examples/post-processing-transform-origin/index.html"
                  title="Post processing scrolling wheel with custom transform origin"
                  target="_blank"
                  >Post processing scrolling wheel with custom transform
                  origin</a
                >
              </p>
              <h3>Advanced render targets usage</h3>
              <p>
                <a
                  href="examples/ping-pong-shading-flowmap/index.html"
                  title="Ping pong shading flowmap example"
                  target="_blank"
                  >Ping pong shading / FBOs swapping flowmap example</a
                ><br />
                <a
                  href="examples/render-passes-using-render-targets/index.html"
                  title="Selective shader passes using render targets"
                  target="_blank"
                  >Selective shader passes using render targets</a
                ><br />
                <a
                  href="examples/gsap-click-to-fullscreen-gallery/index.html"
                  title="GSAP click to fullscreen gallery"
                  target="_blank"
                  >GSAP click to fullscreen gallery</a
                >
              </p>
              <h3>Custom scroll</h3>
              <p>
                <a
                  href="examples/multiple-planes-scroll-effect-custom-scroll/index.html"
                  title="Multiple planes scroll effect with Locomotive scroll"
                  target="_blank"
                  >Multiple planes scroll effect with Locomotive scroll</a
                >
              </p>
            </div>

            <div class="text-section">
              <h2>Knowledge and technical requirements</h2>
              <p>
                It is easy to use but you will of course have to possess good
                basics of HTML, CSS and javascript.
              </p>
              <p>
                If you've never heard about shaders, you may want to learn a bit
                more about them on
                <a
                  href="https://thebookofshaders.com/"
                  title="The Book of Shaders"
                  target="_blank"
                  >The Book of Shaders</a
                >
                for example. You will have to understand what are the vertex and
                fragment shaders, the use of uniforms as well as the GLSL syntax
                basics.
              </p>
            </div>

            <div class="text-section">
              <h2>Installation</h2>
              <div>
                <p>Using npm:</p>

                <div class="code">npm i curtainsjs</div>
              </div>
              <div>
                <p>Load ES modules:</p>

                <div class="code">
                  <span class="code-var">import</span> {<span class="code-tag"
                    >Curtains</span
                  ><span class="code-var">,</span>
                  <span class="code-tag">Plane</span>}
                  <span class="code-var">from</span>
                  <span class="code-string">'curtainsjs'</span
                  ><span class="code-var">;</span>
                </div>

                <p>
                  See the <a href="documentation.html">documentation</a> for a
                  complete list of all classes availables.
                </p>
              </div>
              <div>
                <p>
                  In a browser, you can use the UMD files located in the 'dist'
                  directory:
                </p>

                <div class="code">
                  <span class="code-tag">&lt;script</span> src=<span
                    class="code-string"
                    >"dist/curtains.umd.min.js"</span
                  ><span class="code-tag">&gt;&lt;/script&gt;</span>
                </div>

                <div class="code">
                  <div class="code-block">
                    <div class="code-comment">
                      // "canvas" is the ID of our HTML container element
                    </div>
                    <div>
                      <span class="code-var">const</span> curtains =
                      <span class="code-var">new</span>
                      <span class="code-tag">Curtains</span>({
                      <div class="code-indent">
                        <span class="code-property">container</span>:
                        <span class="code-string">"canvas"</span>
                      </div>
                      })<span class="code-var">;</span>
                    </div>
                  </div>

                  <div>
                    <span class="code-var">const</span> plane =
                    <span class="code-var">new</span>
                    <span class="code-tag">Plane</span>(curtains<span
                      class="code-var"
                      >,</span
                    >
                    document.<span class="code-tag">getElementById</span>(<span
                      class="code-string"
                      >"my-plane"</span
                    >))<span class="code-var">;</span>
                  </div>
                </div>
              </div>

              <h3>Usage with React & Vue</h3>

              <div>
                <p>
                  Note that if you are using React or Vue, you might want to try
                  <a
                    href="https://github.com/martinlaxenaire/react-curtains"
                    title="react-curtains"
                    target="_blank"
                    >react-curtains</a
                  >
                  or
                  <a
                    href="https://github.com/martinlaxenaire/vue-curtains"
                    title="vue-curtains"
                    target="_blank"
                    >vue-curtains</a
                  >, curtains.js official React and Vue packages.
                </p>
              </div>
            </div>

            <div class="text-section">
              <h2>About</h2>
              <p>
                This library is released under the MIT license which means it is
                free to use for personnal and commercial projects.
              </p>
              <p>
                All images used in the examples were taken by
                <a
                  href="https://marionbornaz.com/"
                  title="Marion Bornaz"
                  target="_blank"
                  >Marion Bornaz</a
                >
                during the
                <a
                  href="https://www.miragefestival.com/"
                  title="Mirage Festival"
                  target="_blank"
                  >Mirage Festival</a
                >.
              </p>
              <p>
                All examples video footages were shot by
                <a
                  href="https://analogueprod.com/"
                  title="Analogue Production"
                  target="_blank"
                  >Analogue Production</a
                >.
              </p>
              <p>
                Many thanks to
                <a
                  href="https://webglfundamentals.org/"
                  title="webglfundamentals.org"
                  target="_blank"
                  >webglfundamentals.org</a
                >
                tutorials which helped me a lot.
              </p>
              <p>
                Author of this library is
                <a
                  href="https://www.martin-laxenaire.fr/"
                  title="Martin Laxenaire"
                  target="_blank"
                  >Martin Laxenaire</a
                >, a french creative front-end developper based in Lyon.<br />
                Found a bug ? Have questions ? Do not hesitate to
                <a href="mailto:martin.laxenaire@gmail.com" title="contact me"
                  >email me</a
                >,
                <a
                  href="https://github.com/martinlaxenaire/curtainsjs/issues"
                  title="Fill an issue on Github"
                  >fill an issue on Github</a
                >
                or send me a tweet :
                <a
                  href="https://twitter.com/martinlaxenaire"
                  target="_blank"
                  title="My twitter"
                  >@martinlaxenaire</a
                >.
              </p>
            </div>

            <div id="showcase" class="text-section">
              <h2>Showcase</h2>

              <p>
                Here you'll find websites that use curtains.js with their own
                custom shaders. You can also check all the
                <a
                  href="https://www.awwwards.com/websites/curtains/"
                  title="Websites using curtains.js on Awwwards"
                  target="_blank"
                  rel="noopener noreferrer"
                  >Awwwards' submitted websites using it</a
                >.
              </p>

              <div class="websites-examples-gallery">
                <a
                  class="website-example-item"
                  href="https://www.martin-laxenaire.fr/"
                  title="Martin Laxenaire"
                  target="_blank"
                >
                  <div class="website-example-cover">
                    <div class="showcase-curtain">
                      <img
                        src="images/curtains-js-martin-laxenaire.jpg"
                        alt="Martin Laxenaire"
                        data-sampler="uExample"
                      />
                    </div>
                  </div>
                </a>

                <a
                  class="website-example-item"
                  href="https://jonathanalpmyr.com/"
                  title="Jonathan Alpmyr"
                  target="_blank"
                >
                  <div class="website-example-cover">
                    <div class="showcase-curtain">
                      <img
                        src="images/curtains-js-jonathan-alpmyr.jpg"
                        alt="Jonathan Alpmyr"
                        data-sampler="uExample"
                      />
                    </div>
                  </div>
                </a>

                <a
                  class="website-example-item"
                  href="https://www.miragefestival.com/2019/"
                  title="Mirage Festival 2019"
                  target="_blank"
                >
                  <div class="website-example-cover">
                    <div class="showcase-curtain">
                      <img
                        src="images/curtains-js-mirage-festival-2019.jpg"
                        alt="Mirage Festival 2019"
                        data-sampler="uExample"
                      />
                    </div>
                  </div>
                </a>

                <a
                  class="website-example-item"
                  href="https://www.benjaminhenon.com/"
                  title="Benjamin Hénon"
                  target="_blank"
                >
                  <div class="website-example-cover">
                    <div class="showcase-curtain">
                      <img
                        src="images/curtains-js-benjamin-henon.jpg"
                        alt="Benjamin Hénon"
                        data-sampler="uExample"
                      />
                    </div>
                  </div>
                </a>
              </div>
            </div>
          </div>
        </section>
      </div>
    </div>

    <script id="intro-vs" type="x-shader/x-vertex">
      precision mediump float;

      // default mandatory variables
      attribute vec3 aVertexPosition;
      attribute vec2 aTextureCoord;

      uniform mat4 uMVMatrix;
       		uniform mat4 uPMatrix;

      // custom variables
      varying vec3 vVertexPosition;
      varying vec3 vOriginalVertexPosition;
      varying vec2 vTextureCoord;

      uniform float uMouseTime;
      uniform vec2 uMousePosition;
      uniform float uMouseMoveStrength;


      void main() {

      	vec3 vertexPosition = aVertexPosition;

      	// fix the curtain on top (y coord ranges from top : 1.0 to bottom : -1.0)
      	float yCoordRatio = vertexPosition.y - 1.0;


      	float distanceFromMouse = distance(vec2(uMousePosition.x, 0.0), vec2(vertexPosition.x, 0.0));

      	//float waveSinusoid = cos(10.0 * 1.5 * ((1.0 / (distanceFromMouse - 2.0)) - (uMouseTime / 500.0)));
      	float waveSinusoid = cos(35.0 * ((1.0 / (cos(distanceFromMouse) - 2.0)) - uMouseTime * 0.0015));

      	float xAttenuation = ((2.0 - abs(uMousePosition.x - vertexPosition.x)) / 2.0);
      	float yAttenuation = ((-1.0 * uMousePosition.y) + 1.0) / 2.0;
      	if(uMousePosition.y <= -1.0) {
      		yAttenuation = yAttenuation + (uMousePosition.y + 1.0) * 1.5;
      	}
      	yAttenuation = clamp(yAttenuation, 0.0, 1.0);

      	float curtainEffect = yCoordRatio  * waveSinusoid * uMouseMoveStrength * xAttenuation * yAttenuation;

      	float xDisplacement = abs(yCoordRatio) * sign(vertexPosition.x);

      	vertexPosition.z +=  curtainEffect / 30.0;
      	vertexPosition.x +=  curtainEffect * 1.5 * xDisplacement / 240.0;

         	gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0);

         	// varyings
         	vTextureCoord = aTextureCoord;
         	vVertexPosition = vertexPosition;
                   vOriginalVertexPosition = aVertexPosition;
      }
    </script>
    <script id="intro-fs" type="x-shader/x-fragment">
      precision mediump float;

      varying vec3 vVertexPosition;
      varying vec3 vOriginalVertexPosition;
      varying vec2 vTextureCoord;

      uniform sampler2D curtainSampler;
      uniform sampler2D titleSampler;


      vec3 getNormal(vec3 pos, vec3 originalPos) {
                   float diff = 0.25;
                   vec3 neighbour1 = vec3(originalPos.x + diff, originalPos.y, originalPos.z);
                   vec3 neighbour2 = vec3(originalPos.x, pos.y + diff, originalPos.z);
                   vec3 tangent = (neighbour1 - pos);
                   vec3 bitangent = (neighbour2 - pos);
                   return normalize(cross(tangent, bitangent));
               }


      void main() {

      	vec2 textureCoords = vec2(vTextureCoord.x, vTextureCoord.y);

      	// slides transitions
      	vec4 finalColor = texture2D(curtainSampler, textureCoords);
      	vec4 title = texture2D(titleSampler, textureCoords);

      	finalColor = mix(finalColor, title, title.a);

      	//finalColor.rgb -= clamp(-vVertexPosition.z / 5.0, 0.0, 1.0);
      	//finalColor.rgb += clamp(vVertexPosition.z / 5.0, 0.0, 1.0);

      	vec3 normal = getNormal(vVertexPosition, vOriginalVertexPosition);

                   vec3 lightPos = normalize(vec3(0.3, 0.3, 1.0));
                   float light = smoothstep(0.45, 1.0, dot(normal, lightPos));

                   float lightStrength = 0.35;
                   float ambientLight = 1.0 - lightStrength;
                   finalColor.rgb = finalColor.rgb * light * lightStrength + finalColor.rgb * ambientLight;

      	// handling premultiplied alpha
      	finalColor = vec4(finalColor.rgb * finalColor.a, finalColor.a);

      	gl_FragColor = finalColor;
      }
    </script>

    <script id="simple-shader-vs" type="x-shader/x-vertex">
      precision mediump float;

      // default mandatory variables
      attribute vec3 aVertexPosition;
      attribute vec2 aTextureCoord;

      uniform mat4 uMVMatrix;
       		uniform mat4 uPMatrix;

      // custom variables
      varying vec3 vVertexPosition;
      varying vec2 vTextureCoord;

      uniform float uTime;

      void main() {
      	vec3 vertexPosition = aVertexPosition;

      	// a float varying from -1.5 to 1.5
      	float waveCoords = ((uTime / 45.0) * 3.5) - 1.75;

      	// distance from the waveCoords to the vertex coordinates
      	float distanceToWave = distance(vec2(vertexPosition.x, 0.0), vec2(waveCoords, 0.0));

      	// nice little wave animation from left to right or right to left depending on the timer
      	vertexPosition.z -= (cos(clamp(distanceToWave, 0.0, 0.75) * 3.141592) - cos(0.75 * 3.141592) + (2.0 * sin(3.141592 * uTime / 90.0))) * 0.025;

         	gl_Position = uPMatrix * uMVMatrix * vec4(vertexPosition, 1.0);

         	// varyings
         	vTextureCoord = aTextureCoord;
         	vVertexPosition = vertexPosition;
      }
    </script>
    <script id="simple-shader-fs" type="x-shader/x-fragment">
      precision mediump float;

      uniform float uTime;

      varying vec3 vVertexPosition;
      varying vec2 vTextureCoord;

      uniform sampler2D uExample;


      void main() {

      	// get our texture coords
      	vec2 textureCoords = vec2(vTextureCoord.x, vTextureCoord.y);
      	vec4 finalColor = texture2D(uExample, textureCoords);

      	gl_FragColor = finalColor;
      }
    </script>

    <script src="js/main.navigation.js" type="module"></script>

    <!-- Global site tag (gtag.js) - Google Analytics -->
    <script
      async
      src="https://www.googletagmanager.com/gtag/js?id=UA-141413267-1"
    ></script>
    <script>
      window.dataLayer = window.dataLayer || [];
      function gtag() {
        dataLayer.push(arguments);
      }
      gtag("js", new Date());

      gtag("config", "UA-141413267-1");
    </script>

    <script type="text/javascript">
      document
        .getElementById("download-full-lib-link")
        .addEventListener("click", function () {
          gtag("event", "click", {
            event_category: "engagement",
            event_label: "Downloaded full lib",
          });
        });

      document
        .getElementById("download-min-lib-link")
        .addEventListener("click", function () {
          gtag("event", "click", {
            event_category: "engagement",
            event_label: "Downloaded minified lib",
          });
        });
    </script>
  </body>
</html>
